package com.bluesky.jwf.component.window;

import java.util.ArrayList;
import java.util.List;

import com.bluesky.common.StringUtil;
import com.bluesky.javawebbrowser.domain.html.tags.Div;
import com.bluesky.javawebbrowser.domain.html.tags.Tag;
import com.bluesky.jwf.ComponentFactory;
import com.bluesky.jwf.component.Component;
import com.bluesky.jwf.component.dialog.MessageDialog;
import com.bluesky.jwf.component.event.AbstractEventHandler;
import com.bluesky.jwf.component.event.EventHandler;
import com.bluesky.jwf.component.validator.Validator;

/**
 * it is the parent window's responsibility to decide how to present it self and
 * its children window. and the children windows can be overlapped, or in a tab
 * style. there is a root window,like the 'screen window' concept in Windows.
 * the root window have no parent. <br>
 * generally, children window are displayed in a so called child window area. by
 * default, this class do not implement a reserved children window area.
 * 
 * @author hz00260
 * 
 */
public class Window extends Component {
	public static String EVENT_CLOSE = "close";

	protected String title;
	/**
	 * String type because they are css attributes, size of document.
	 */
	protected String left;
	protected String top;
	protected String width;
	protected String height;
	protected int zOrder;

	/**
	 * size of browser window(physical window, not logical window).
	 */
	protected int physicalWidth;
	protected int physicalHeight;

	/**
	 * the out side Tag of window must be DIV. so we can emulate as Dialog
	 */
	protected Div windowTag;
	protected Tag headTag;
	protected Tag bodyTag;
	protected WindowHead head;

	protected EventHandler queryCloseHandler;
	protected EventHandler closeHandler;

	protected Window parentWindow;

	protected boolean destroyOnClose;
	/*
	 * the childrenWindow is ordered by Z axis.
	 */
	protected List<Window> childrenWindow = new ArrayList<Window>();

	protected boolean validateResult;

	protected int renderCount = 0;

	public Window() {
		destroyOnClose = true;
	}

	@Override
	public void init(String html) {

		super.init(html);

		windowTag = (Div) loadByJwfClass("window");
		headTag = windowTag.getByJwfClass("head");
		// if not found, assign a head as the first child.
		if (headTag == null) {
			head = (WindowHead) ComponentFactory.getInstance().createComponent(
					WindowHead.class.getSimpleName());
			windowTag.addChild(0, head);
		} else {
			head = (WindowHead) ComponentFactory.getInstance().createComponent(
					WindowHead.class.getSimpleName(), headTag.toHtml());
			headTag.replace(head);
		}
		head.setWindow(this);

		// clear anything in the body tag, it is a container.
		bodyTag = windowTag.loadByJwfClass("body");

		// bodyTag.removeAllChildren();

	}

	public String getLeft() {
		return left;
	}

	public void setLeft(String left) {

		this.left = left;
		windowTag.setStyleAttribute("left", left);
	}

	public String getTop() {
		return top;
	}

	public void setTop(String top) {
		this.top = top;
		windowTag.setStyleAttribute("top", top);
	}

	public String getWidth() {
		return width;
	}

	public void setWidth(String width) {
		this.width = width;
		windowTag.setStyleAttribute("width", width);
	}

	public String getHeight() {
		return height;
	}

	public void setHeight(String height) {
		this.height = height;
		windowTag.setStyleAttribute("height", height);
	}

	public int getZOrder() {
		return zOrder;
	}

	public void setZOrder(int order) {
		zOrder = order;
		windowTag.setStyleAttribute("z-index", String.valueOf(order));
	}

	public int getPhysicalWidth() {
		return physicalWidth;
	}

	public void setPhysicalWidth(int physicalWidth) {
		this.physicalWidth = physicalWidth;
	}

	public int getPhysicalHeight() {
		return physicalHeight;
	}

	public void setPhysicalHeight(int physicalHeight) {
		this.physicalHeight = physicalHeight;
	}

	public Div getWindowTag() {
		return windowTag;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
		if (head != null)
			head.setTitle(title);
	}

	public void addChildWindow(Window child) {
		if (childrenWindow.contains(child))
			throw new RuntimeException(
					"addChildWindow() fail, it is already the child.");

		childrenWindow.add(child);
		child.parentWindow = this;
	}

	public void addChildWindow(int index, Window child) {
		if (childrenWindow.contains(child))
			throw new RuntimeException(
					"addChildWindow() fail, it is already the child.");

		childrenWindow.add(index, child);
		child.parentWindow = this;
	}

	public void removeChildWindow(Window child) {
		if (!childrenWindow.contains(child))
			throw new RuntimeException(
					"removeChildWindow() fail, it is not a child.");

		childrenWindow.remove(child);
		child.parentWindow = null;
	}

	public Tag getByIdInChildWindow(final String id) {
		Tag result = this.getById(id);

		if (result != null)
			return result;

		for (Window w : getChildrenWindow()) {
			result = w.getByIdInChildWindow(id);
			if (result != null)
				return result;

		}
		return null;
	}

	/**
	 * return null if no child at all.
	 * 
	 * @return
	 */
	public Window getMostFrontChildWindow() {
		if (getChildrenWindow().isEmpty())
			return null;

		return getChildrenWindow().get(getChildrenWindow().size() - 1);
	}

	public void adjustChildWindowZOrder(WindowZOrderOperation op, Window child) {
		if (getChildrenWindow().contains(child)) {
			if (op == WindowZOrderOperation.MostFront) {
				removeChildWindow(child);
				addChildWindow(child);

			} else if (op == WindowZOrderOperation.MostBehind) {
				removeChildWindow(child);
				addChildWindow(0, child);

			}
		}
	}

	public Window getRootWindow() {
		if (parentWindow == null)
			return this;

		return parentWindow.getRootWindow();
	}

	public Window getParentWindow() {
		return parentWindow;
	}

	public void setParentWindow(Window parentWindow) {
		this.parentWindow = parentWindow;
	}

	public List<Window> getChildrenWindow() {
		return childrenWindow;
	}

	public void setChildrenWindow(List<Window> childrenWindow) {
		this.childrenWindow = childrenWindow;
	}

	public void close() {
		if (queryCloseHandler != null)
			queryCloseHandler.handle(this, null, new AbstractEventHandler() {
				@Override
				public void handle(Component target, Object parameter) {
					if (closeHandler != null)
						closeHandler.handle(target, null, null);

					if (destroyOnClose)
						destroy();

				}
			});
		else {
			if (closeHandler != null)
				closeHandler.handle(this, null, null);

			if (destroyOnClose)
				destroy();
		}

	}

	public void destroy() {
		if (parentWindow != null)
			parentWindow.removeChildWindow(this);
	}

	public EventHandler getCloseHandler() {
		return closeHandler;
	}

	public void setCloseHandler(EventHandler handler) {
		this.closeHandler = handler;
		eventHandlers.put(EVENT_CLOSE, handler);
	}

	public void registerDialog(Window window) {
		Window rootWindow = getRootWindow();
		if (!(rootWindow instanceof ScreenWindow))
			throw new RuntimeException(
					"fail to register dialog, the root window is:" + rootWindow);

		rootWindow.addChildWindow(window);
		// make sure it on the top
		rootWindow.adjustChildWindowZOrder(WindowZOrderOperation.MostFront,
				window);

	}

	public boolean validate() {
		validateResult = true;
		traverse(new Tag.TagProcessor() {
			@Override
			public boolean process(Tag tag) {
				if (tag instanceof Validator) {
					Validator v = (Validator) tag;
					v.validate();

					if (!v.getResult())
						validateResult = false;
				}
				return false;
			}
		});
		return validateResult;
	}

	public Tag getHeadTag() {
		return headTag;
	}

	public void setHeadTag(Tag headTag) {
		this.headTag = headTag;
	}

	public Tag getBodyTag() {
		return bodyTag;
	}

	public void setBodyTag(Tag bodyTag) {
		this.bodyTag = bodyTag;
	}

	public void setWindowTag(Div windowTag) {
		this.windowTag = windowTag;
	}

	public EventHandler getQueryCloseHandler() {
		return queryCloseHandler;
	}

	public void setQueryCloseHandler(EventHandler queryCloseHandler) {
		this.queryCloseHandler = queryCloseHandler;
	}

	public void showMessage(String msg) {
		MessageDialog dlg = (MessageDialog) ComponentFactory.getInstance()
				.createComponent(MessageDialog.class.getSimpleName());
		dlg.setMessage(msg);
		registerDialog(dlg);
	}

	public int getWindowDepth() {
		if (parentWindow == null)
			return 0;
		else
			return parentWindow.getWindowDepth() + 1;

	}

	/*
	 * include itself and all descendants
	 */
	public void getAllWindows(List<Window> list) {
		list.add(this);

//		StringBuilder sb = new StringBuilder();
//		for (int i = 0; i < getWindowDepth(); i++)
//			sb.append(" ");
//
//		System.out.println(sb.toString() + this);

		for (Window w : childrenWindow)
			w.getAllWindows(list);
	}

	public void onRender() {
		renderCount++;

		// first time render, it is a huge event
		if (renderCount == 1)
			onShow();
	}

	public void onShow() {
		// intend to do nothing here
	}

	@Override
	public String toString() {

		return String.format("id:%s,title:%s", getId(), getTitle());
	}
}
